import socket from '@ohos.net.socket'
import wifi from "@ohos.wifi"

import Protocol, { ProtocolResult, ProtocolPacket, ProtocolDeviceInfo, ProtocolStringRes, ProtocolEcg, ProtocolCmd } from './Protocol'
import { AlertDialog } from '@kit.ArkUI'
import {UIContext} from '@ohos.arkui.UIContext';

// import { AlertDialog } from '@kit.ArkUI'

// 定义医疗检测仪的状态信息结构体
export type UdpProcess_RemoteDeviceStatus = {
  remote_info: socket.SocketRemoteInfo,
  device_info: ProtocolDeviceInfo,
}

class UdpProcess {
  public remote_port: number = 8888     // 医疗检测仪的监听端口
  public local_ipaddr: string           // 本地IP地址
  public local_port: number = 9999      // 本地监听端口
  public udp: socket.UDPSocket          // udp对象

  // 存放远端的IP地址等信息
  public remote_device_status: Array<UdpProcess_RemoteDeviceStatus> = []
  public remote_device_select: number = 0

  /***************************************************************
   * 函数名称: constructor
   * 说    明: 类初始化时，触发该函数
   *          初始化TCP，并配置接收回调函数
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  constructor() {
    this.udp = socket.constructUDPSocketInstance()
    this.local_ipaddr = this.ipNumber2String(wifi.getIpInfo().ipAddress)

    // 清空远端的IP地址信息
    this.remote_device_status = []

    // 接收数据
    this.udp.on('message', this.recv_message)
    // 开启监听
    this.udp.on('listening', this.listening)
    // 开启监听
    this.udp.bind({ address: "0.0.0.0", port: this.local_port, family: 1 }, err => {
      if (err) {
        console.log("UdpProcess: bind failed")
      } else {
        console.log("UdpProcess: bind success")
      }
    })
  }

  /***************************************************************
   * 函数名称: ipNumber2String
   * 说    明: 将数字的IP地址转化为字符串
   * 参    数:
   *      @ip     number类型的IP地址
   * 返 回 值: 返回string类型的IP地址
   ***************************************************************/
  ipNumber2String(ip: number) : string {
    if (ip < 0 || ip > 0xFFFFFFFF) {
      throw ("The number is not normal!");
    }
    return (ip >>> 24) + "." + (ip >> 16 & 0xFF) + "." + (ip >> 8 & 0xFF) + "." + (ip & 0xFF);
  }

  /***************************************************************
   * 函数名称: arrayBuffer2String
   * 说    明: 将ArrayBuffer转为string
   * 参    数:
   *      @message     ArrayBuffer类型的字符串
   * 返 回 值: 返回string类型的字符串
   ***************************************************************/
  arrayBuffer2String(message: ArrayBuffer): string {
    if (message instanceof ArrayBuffer) {
      let dataView = new DataView(message)
      let str = ""
      for (let i = 0;i < dataView.byteLength; ++i) {
        let c = String.fromCharCode(dataView.getUint8(i))
        str += c
      }
      return str;
    }
  }

  /***************************************************************
   * 函数名称: string2ArrayBuffer
   * 说    明: 将string类型转化为ArrayBuffer类型
   * 参    数:
   *      @str     string类型的字符串
   * 返 回 值: 返回ArrayBuffer类型的字符串
   ***************************************************************/
  string2ArrayBuffer(str: string): ArrayBuffer {
    var bytes = new Array();
    var len, c;
    len = str.length;
    for (var i = 0; i < len; i++) {
      c = str.charCodeAt(i);
      if (c >= 0x010000 && c <= 0x10FFFF) {
        bytes.push(((c >> 18) & 0x07) | 0xF0);
        bytes.push(((c >> 12) & 0x3F) | 0x80);
        bytes.push(((c >> 6) & 0x3F) | 0x80);
        bytes.push((c & 0x3F) | 0x80);
      } else if (c >= 0x000800 && c <= 0x00FFFF) {
        bytes.push(((c >> 12) & 0x0F) | 0xE0);
        bytes.push(((c >> 6) & 0x3F) | 0x80);
        bytes.push((c & 0x3F) | 0x80);
      } else if (c >= 0x000080 && c <= 0x0007FF) {
        bytes.push(((c >> 6) & 0x1F) | 0xC0);
        bytes.push((c & 0x3F) | 0x80);
      } else {
        bytes.push(c & 0xFF);
      }
    }
    var array = new Int8Array(bytes.length);
    for (var i = 0; i <= bytes.length; i++) {
      array[i] = bytes[i];
    }
    return array.buffer;
  }


  /***************************************************************
   * 函数名称: remoteInfoPrint
   * 说    明: 打印所有的远端的IP地址
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  remoteInfoPrint() {
    let i: number = 0

    // 打印所有
    console.log("UdpProcess: remoteInfo.length = " + this.remote_device_status.length)
    for (i = 0; i < this.remote_device_status.length; i++) {
      console.log("UdpProcess: " + i
      + " => address = " + this.remote_device_status[i].remote_info.address
      + ", deviceId = " + this.remote_device_status[i].device_info.deviceId)
    }
  }

  /*************************************************************************
   * 函数名称: getIndex_RemoteDeviceStatus
   * 说    明: 返回远端设备列表的相对位置。如果没有，则添加
   * 参    数:
   *      @remoteInfo   医疗检测仪的IP地址等相关信息
   *      @deviceId     医疗检测仪的设备编号
   * 返 回 值: 返回remoteInfo在remote_device_status队列中的偏移位置，返回-1为失败
   *************************************************************************/
  getIndex_RemoteDeviceStatus(remoteInfo: socket.SocketRemoteInfo, deviceId: string) : number {
    let i: number
    for (i = 0; i < this.remote_device_status.length; i++) {
      if (remoteInfo.address == this.remote_device_status[i].remote_info.address) {
        return i
      }
    }
    if (i >= this.remote_device_status.length) {
      let remote_info: socket.SocketRemoteInfo = remoteInfo
      let device_info: ProtocolDeviceInfo = {
        deviceId: deviceId,
        heart_rate_is_valid: false,
        heart_rate: 0,
        blood_oxygen_is_valid: false,
        blood_oxygen: 0,
        body_temperature_is_valid: false,
        body_temperature: 0,
        ecg: [],
      }
      let status: UdpProcess_RemoteDeviceStatus = {
        remote_info: remoteInfo,
        device_info: device_info,
      }
      this.remote_device_status.push(status)
    }
    return i
  }

  /***************************************************************
   * 函数名称: remoteInfoAdd
   * 说    明: 添加到客户端的IP地址
   * 参    数:
   *      @remoteInfo   医疗检测仪的IP地址等相关信息
   *      @deviceId     医疗检测仪的设备编号
   * 返 回 值: 无
   *************************************************************************/
  remoteInfoAdd(remoteInfo: socket.SocketRemoteInfo, deviceId: string) {
    let i: number = this.getIndex_RemoteDeviceStatus(remoteInfo, deviceId)
  }


  /***************************************************************
   * 函数名称: recv_message
   * 说    明: UDP接收消息的响应函数
   * 参    数:
   *      @value    包括接收消息的IP地址信息 + 接收到的信息
   * 返 回 值: 无
   ***************************************************************/
  recv_message = value => {
    console.log("UdpProcess => recv message: " + this.arrayBuffer2String(value.message))
    console.log("UdpProcess => remote: " + JSON.stringify(value.remoteInfo))
    let remoteInfo: socket.SocketRemoteInfo = value.remoteInfo
    let message: string = this.arrayBuffer2String(value.message)

    // 解包
    let packet = Protocol.parsePacket(message)
    if (packet.result != ProtocolResult.ProtocolResult_Success) {
      console.log("UdpProcess: Protocol.parsePacket failed and result = ", packet.result)
      return
    }

    this.remoteInfoPrint()
    console.log("UdpProcess: packet.command = " + packet.cmd)
    switch (packet.cmd) {
      case ProtocolCmd.ProtocolCmd_FindDevice:
        // 添加远端IP地址信息
        this.remoteInfoAdd(remoteInfo, packet.deviceId)
        break;

      case ProtocolCmd.ProtocolCmd_ReportHeartRateAndBloodOxygen: {
        let data = Protocol.reportHeartRateAndBloodOxygen(message)
        if (data.result !== ProtocolResult.ProtocolResult_Success) {
          console.log("UdpProcess: cmd_reportHeartRateAndBloodOxygen and result is error")
          return
        }
        let i: number = this.getIndex_RemoteDeviceStatus(remoteInfo, data.deviceId)
        this.remote_device_status[i].device_info.heart_rate_is_valid = data.heart_rate_is_valid
        this.remote_device_status[i].device_info.heart_rate = data.heart_rate
        this.remote_device_status[i].device_info.blood_oxygen_is_valid = data.blood_oxygen_is_valid
        this.remote_device_status[i].device_info.blood_oxygen = data.blood_oxygen
      }
        break;

      case ProtocolCmd.ProtocolCmd_ReportBodyTemperature: {
        let data = Protocol.reportBodyTemperature(message)
        if (data.result !== ProtocolResult.ProtocolResult_Success) {
          console.log("UdpProcess: cmd_reportBodyTemperature and reesult is error")
          return
        }
        let i: number = this.getIndex_RemoteDeviceStatus(remoteInfo, data.deviceId)
        this.remote_device_status[i].device_info.body_temperature_is_valid = data.temperature_is_valid
        this.remote_device_status[i].device_info.body_temperature = data.temperature
        console.log("UdpProcess: 收到主动上报报文，并且解析成功")
      }
      break;

      case ProtocolCmd.ProtocolCmd_ReportECG: {
        let data = Protocol.reportECG(message)
        if (data.result !== ProtocolResult.ProtocolResult_Success) {
          console.log("UdpProcess: cmd_reportECG and result is error")
          return
        }
        console.info("UdpProtocess: ecg_data push")
        let i: number = this.getIndex_RemoteDeviceStatus(remoteInfo, data.deviceId)
        this.remote_device_status[i].device_info.heart_rate_is_valid = true
        this.remote_device_status[i].device_info.heart_rate = data.ecg.heart_rate
        this.remote_device_status[i].device_info.ecg.push(data.ecg)
        // 删除过往的数据
        if (this.remote_device_status[i].device_info.ecg.length >= 3) {
          let n: number = 0
          for (n = 0; n < (this.remote_device_status[i].device_info.ecg.length - 3); n++) {
            var ecg_temp = this.remote_device_status[i].device_info.ecg.shift()
          }
        }
      }
      break;

      default:
        console.log("UdpProcess: packet.command " + packet.cmd + " out of the range")
        break;
    }
  }

  /***************************************************************
   * 函数名称: listening
   * 说    明: 监听成功的响应函数
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  listening() {
    console.log("UdpProcess => listening success")
  }

  /***************************************************************
   * 函数名称: send
   * 说    明: 通过UDP发送协议报文
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  send(remote_ipaddr: string, remote_port: number, message: string) {
    this.udp.send(
      {
        data: this.string2ArrayBuffer(message),
        address: {
          address: remote_ipaddr,
          port: remote_port,
          family: 1
        }
      },
      error => {
        if (error) {
          console.log("UdpProcess: send failed and ip(" + remote_ipaddr + "), port(" + remote_port + "), message(" + message + ")")
        } else {
          //console.log("UdpProcess: send success")
        }
      }
    )
  }

  /***************************************************************
   * 函数名称: close
   * 说    明: 当关闭UDP连接时，自动调用该函数
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  close() {
    this.udp.close(error => {
      if (error) {
        console.log("UdpProcess: close failed")
      } else {
        console.log("UdpProcess: close success")
      }
    })
  }

  /***************************************************************
   * 函数名称: getEcgData
   * 说    明: 获取心电图数据
   * 参    数: 无
   * 返 回 值: 返回ProtocolEcg类型的心电图数据
   ***************************************************************/
  getEcgData() : ProtocolEcg  {
    let offset: number = this.remote_device_select
    let ecg: ProtocolEcg = {
      heart_rate: 0,
      data: []
    }
    if (this.checkDeviceId() == false) {
      console.error("UdpProcess: checkDeviceId() is error")
      return ecg
    }
    if (this.remote_device_status[offset].device_info.ecg.length >= 1) {
      // 删除以往的，只保留最新的
      let i: number = 0
      for (i = 0; i < this.remote_device_status[offset].device_info.ecg.length; i++) {
        ecg = this.remote_device_status[offset].device_info.ecg.shift()
      }
    }
    return ecg
  }

  /***************************************************************
   * 函数名称: findOffsetFromDeviceId
   * 说    明: 通过deviceId查找remote_device_status数组的偏移位置
   * 参    数:
   *      @deviceId     医疗检测仪的设备编号
   * 返 回 值: 返回偏移位置，返回-1则为失败
   ***************************************************************/
  findOffsetFromDeviceId(deviceId: string): number {
    let i: number = 0

    for (i = 0; i < this.remote_device_status.length; i++) {
      if (deviceId == this.remote_device_status[i].device_info.deviceId) {
        return i
      }
    }

    return (-1)
  }

  /***************************************************************
   * 函数名称: showAlertDialog
   * 说    明: 报警对话框
   * 参    数:
   *      @title      对话框的标题
   *      @message    对话框的内容
   *      @confirm    对话框的确定按键的显示内容
   *      @action     对话框的确定按键后打印信息
   *      @cancel     对话框的取消按键后打印信息
   * 返 回 值: 无
   ***************************************************************/
  // showAlertDialog(title: string, message: string, confirm: string, action: string, cancel: string) {
  //   let alertDlg: AlertDialog = new AlertDialog()
  //   // UIContext.showDialog
  //   alertDlg.show(
  //     {
  //       title: title,
  //       message: message,
  //       autoCancel: true,
  //       alignment: DialogAlignment.Bottom,
  //       offset: { dx: 0, dy: -20 },
  //       gridCount: 3,
  //       confirm: {
  //         value: confirm,
  //         action: () => {
  //           console.info('WisdomHouse: ' + action)
  //         }
  //       },
  //       cancel: () => {
  //         console.info('WisdomHouse: ' + cancel)
  //       }
  //     }
  //   )
  // }

  /***************************************************************
   * 函数名称: findDevice
   * 说    明: 查找局域网内的其他设备
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  findDevice() {
    let i: number = 0
    let ip: string
    // 重新获取IP地址
    this.local_ipaddr = this.ipNumber2String(wifi.getIpInfo().ipAddress)
    var ip_address = this.local_ipaddr.split('.', 3)
    let value = Protocol.findDevice()

    // 清除当前发现的设备
    this.remote_device_status = []

    // console.log("UdpProcess: value.packet = " + value.packet)
    // for (i = 0; i < value.packet.length; i++) {
    //   console.log("UdpProcess: [" + i + "] = " + value.packet.charCodeAt(i))
    // }

    // 检查IP地址是否对
    console.log("UdpProcess: " + ip_address[0] + " " + ip_address[1] + " " + ip_address[2])
    if ((ip_address[0] === "0")
      && (ip_address[1] === "0")
      && (ip_address[2] === "0")) {
      // this.showAlertDialog("查找设备错误",
      //   "本地IP地址获取失败，IP地址：" + this.local_ipaddr,
      //   "确定",
      //   "本地IP地址获取失败，IP地址：" + this.local_ipaddr,
      //   "本地IP地址获取失败，IP地址：" + this.local_ipaddr,
      // )
      console.error(`UdpProcess: findDevice => ip_address(${ip_address[0]} ${ip_address[1]} ${ip_address[2]}) is error`)
      return
    }

    for (i = 1; i < 255; i++) {
      ip = ip_address[0] + '.' + ip_address[1] + '.' + ip_address[2] + '.' + i.toLocaleString()
      console.log("UdpProcess: ipaddr = " + ip)
      this.send(ip, this.remote_port, value.packet)
    }
  }

  /***************************************************************
   * 函数名称: checkDeviceId
   * 说    明: 判断当前是否是有效设备
   * 参    数: 无
   * 返 回 值: 无
   ***************************************************************/
  checkDeviceId(): boolean {
    if (this.remote_device_status.length == 0) {
      console.error("UdpProcess: remote_device_status.length = 0")
      return false
    }
    if (this.remote_device_status.length <= this.remote_device_select) {
      console.error("UdpProcess: remote_device_status.length <= remote_device_select")
      return false
    }
    return true
  }

  /***************************************************************
   * 函数名称: getSelectDeviceId
   * 说    明: 获取当前被选的设备号
   * 参    数: 无
   * 返 回 值:
   *      @result     返回结果，true为成功
   *      @deviceId   返回医疗检测仪的设备编号
   ***************************************************************/
  getSelectDeviceId(): {result: boolean, deviceId: string} {
    if (this.checkDeviceId() == false) {
      return {result: false, deviceId: ""}
    }

    return {result: true, deviceId: this.remote_device_status[this.remote_device_select].device_info.deviceId}
  }

  /***************************************************************
   * 函数名称: setDetectionType
   * 说    明: 设置检测类型
   * 参    数:
   *      @deviceId         需要设置检测类型的医疗检测仪的设备号
   *      @detection_type   需要设置检测类型的检测类型
   * 返 回 值: 0为成功，反之为失败
   ***************************************************************/
  setDetectionType(deviceId: string, detection_type: number) : number {
    let offset: number = this.findOffsetFromDeviceId(deviceId)

    if (offset < 0) {
      console.error("UdpProcess: offset = " + offset)
      return ProtocolResult.ProtocolResult_SelectError
    }

    let value = Protocol.setDetectionType(deviceId, detection_type)
    if (value.result != ProtocolResult.ProtocolResult_Success) {
      console.error("UdpProcess: result = " + value.result)
      return value.result
    }

    console.log("UdpProcess: remote_address=" + this.remote_device_status[offset].remote_info.address)
    console.log("UdpProcess: remote_port=" + this.remote_port)
    this.send(this.remote_device_status[offset].remote_info.address,
      this.remote_port,
      value.packet)
    return ProtocolResult.ProtocolResult_Success
  }
}
export default new UdpProcess()